
"""
很多情况下，一些网站的页面或资源我们通常需要登录才能看到。比如说访问 GitHub 的个人设置页面，如果不登录是无法查看的；比如说 12306 买票提交订单的页面，如果不登录是无法提交订单的；比如说要发一条微博，如果不登录是无法发送的。

"""

"""
 网站登录验证的实现
 登录一般是需要两个内容，用户名和密码，有的网站可能是手机号和验证码，有的是微信扫码，有的是 OAuth 验证等等，但根本上来说，都是把一些可供认证的信息提交给了服务器
 服务端校验通过过，返回登录通过的凭证，接下来所有请求携带这个凭证，服务端校验没问题，就可以执行对应的操作
 
 那么问题来了，这个「凭证」到底是怎么生成和验证的呢？目前比较流行的实现方式有两种，一种是基于 Session + Cookie 的验证，一种是基于 JWT（JSON Web Token）的验证，下面我们来介绍下。
"""

"""
 Session和Cookie
 
 我们以用户登录的情形来说吧，其实不同的网站对于用户的登录状态的实现是可能不同的，但是 Session 和 Cookie 一定是相互配合工作的。

下面梳理如下：

比如说，Cookie 里面可能只存了 Session ID 相关信息，服务器能根据 Cookie 找到对应的 Session，用户登录之后，服务器会把对应的 Session 里面标记一个字段，代表已登录状态或者其他信息（如角色、登录时间）等等，这样用户每次访问网站的时候都带着 Cookie 来访问，服务器就能找到对应的 Session，然后看一下 Session 里面的状态是登录状态，那就可以返回对应的结果或执行某些操作。


当然 Cookie 里面也可能直接存了某些凭证信息。比如说用户在发起登录请求之后，服务器校验通过，返回给客户端的 Response Headers 里面可能带有 Set-Cookie 字段，里面可能就包含了类似凭证的信息，这样客户端会执行设置 Cookie 的操作，将这些信息保存到 Cookie 里面，以后再访问网页时携带这些 Cookie 信息，服务器拿着这里面的信息校验，自然也能实现登录状态检测了。
以上两种情况几乎能涵盖大部分的 Session 和 Cookie 登录验证的实现，具体的实现逻辑因服务器而异，但 Session 和 Cookie 一定是需要相互配合才能实现的。

"""

"""
JWT
为实现前后端分离 JWT 技术应运而生。 
JWT，英文全称为 JSON Web Token，是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准。实际上就是在每次登录的时候通过一个 Token 字符串来校验登录状态
JWT 的声明一般被用来在身份提供者和服务提供者之间传递被认证的用户身份信息，以便于从资源服务器获取资源，也可以增加一些额外的业务逻辑所必须的声明信息，所以这个 Token 也可直接被用于认证，也可传递一些额外信息

有了 JWT，一些认证就不需要借助于 Session 和 Cookie 了，服务器也无须维护 Session 信息，减少了服务器的开销。服务器只需要有一个校验 JWT 的功能就好了，同时也可以做到分布式部署和跨语言的支持。
JWT 通常就是一个加密的字符串，它也有自己的标准，类似下面的这种格式：
eyJ0eXAxIjoiMTIzNCIsImFsZzIiOiJhZG1pbiIsInR5cCI6IkpXVCIsImFsZyI6IkhTMjU2In0.eyJVc2VySWQiOjEyMywiVXNlck5hbWUiOiJhZG1pbiIsImV4cCI6MTU1MjI4Njc0Ni44Nzc0MDE4fQ.pEgdmFAy73walFonEm2zbxg46Oth3dlT02HR9iVzXa8

我们可以发现中间有两个用来分割的 . ，因此可以把它看成是一个三段式的加密字符串。
它由三部分构成，分别是 Header、Payload、Signature。

Header，声明了 JWT 的签名算法，如 RSA、SHA256 等，也可能包含 JWT 编号或类型等数据，然后对整个信息进行 Base64 编码即可。
Payload，通常用来存放一些业务需要但不敏感的信息，如 UserID 等，另外它也有很多默认是字段，如 JWT 签发者、JWT 接受者、JWT 过期时间等，Base64 编码即可。
Signature，就是一个签名，是把 Header、Payload 的信息用秘钥 secret 加密后形成的，这个 secret 是保存在服务器端的，不能被轻易泄露。如此一来，即使一些 Payload 的信息被篡改，服务器也能通过 Signature 判断出非法请求，拒绝服务。

这三部分通过 . 组合起来就形成了 JWT 的字符串，就是用户的访问凭证。

所以这个登录认证流程也很简单了，用户拿着用户名密码登录，然后服务器生成 JWT 字符串返回给客户端。
客户端每次请求都带着这个 JWT 就行了，服务器会自动判断其有效情况，如果有效，自然就返回对应的数据。JWT 的传输就多种多样了，
可以将其放在 Request Headers 中，也可以放在 URL 里，甚至也有的网站把它放在 Cookie 里面，但总而言之，能传给服务器进行校验就好了。
好，到此为止呢，我们就已经了解了网站登录验证的实现了。
"""

"""
  基于 Session 和 Cookie 的模拟登录  如何实现?
  
  第一，如果我们已经在浏览器中登录了自己的账号，要想用爬虫模拟，那么可以直接把 Cookie 复制过来交给爬虫。这是最省时省力的方式，相当于我们用浏览器手动操作登录了。我们把 Cookie 放到代码里，
  爬虫每次请求的时候再将其放到 Request Headers 中，完全模拟了浏览器的操作。之后服务器会通过 Cookie 校验登录状态，如果没问题，自然就可以执行某些操作或返回某些内容了。
  
  第二，如果我们不想有任何手工操作，那么可以直接使用爬虫模拟登录过程。其实登录的过程多数也是一个 POST 请求。我们用爬虫提交了用户名、密码等信息给服务器，服务器返回的 Response Headers 里面可能会带有 Set-Cookie 的字段，
  我们只需要把这些 Cookie 保存下来就行了。所以，最主要的就是把这个过程中的 Cookie 维持好。当然这里可能会遇到一些困难，比如登录过程中伴随着各种校验参数，不好直接模拟请求；
  网站设置 Cookie 的过程是通过 JavaScript 实现的，所以可能还得仔细分析下其中的逻辑，尤其是我们用 requests 这样的请求库进行模拟登录的时候，遇到的问题经常比较多。

  第三，我们也可以用一些简单的方式来实现模拟登录，即实现登录过程的自动化。比如我们用 Selenium、Pyppeteer 或 Playwright 来驱动浏览器模拟执行一些操作，如填写用户名和密码、提交表单等。登录成功后，
  通过 Selenium 或 Pyppeteer 获取当前浏览器的 Cookie 并保存即可。这样后续就可以拿着 Cookie 的内容发起请求，同样也能实现模拟登录。

 以上介绍的就是一些常用的爬虫模拟登录的方案，其目的是维护好客户端的 Cookie 信息。总之，每次请求都携带好 Cookie 信息就能实现模拟登录了。

"""

"""
 基于 JWT的模拟登录 如何实现?
 
 第一步，模拟网站登录操作的请求。比如拿着用户名和密码信息请求登录接口，获取服务器返回的结果，这个结果中通常包含 JWT 字符串的信息，将它保存即可。
 第二步，后续的请求携带 JWT 进行访问。在 JWT 不过期的情况下，通常能正常访问和执行对应的操作。携带方式多种多样，因网站而异。
 第三步，如果 JWT 过期了，可能需要再次进行第一步，重新获取 JWT。
 当然，模拟登录的过程肯定会带有一些其他的加密参数，需要根据实际情况具体分析。
"""

"""
 风控问题，如何解决? 答案就是账号池
 如果爬虫要求爬取的数据量比较大或爬取速度比较快，而网站又有单账号并发限制或者访问状态检测等反爬虫手段，那么我们的账号可能就会无法访问或者面临封号的风险了。

 这时候一般怎么办呢？

我们可以使用分流的方案来实现。假设某个网站设置一分钟之内检测到同一个账号访问 3 次或 3 次以上则封号，
我们就可以建立一个账号池，用多个账号来随机访问或爬取数据，这样就能大幅提高爬虫的并发量，降低被封号的风险了。
比如我们可以准备 100 个账号，然后 100 个账号都模拟登录，把对应的 Cookie 或 JWT 存下来，每次访问的时候随机取一个来，
由于账号多，所以每个账号被取用的概率也就降下来了，这样就能避免单账号并发过大的问题，也降低封号风险。
"""